/*
 * Created by Jaren at 2022/01/29 16:53
 */
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:ble_plugin/ble_plugin.dart';
import 'package:ble_plugin/ble_uuid.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:inmotion_protocol/common/exceptions.dart';
import 'package:inmotion_protocol/common/fw_update_manager.dart';
import 'package:inmotion_protocol/common/vehicle_info.dart';
import 'package:inmotion_protocol/util/byte_util.dart';
import 'package:inmotion_protocol/util/device.dart';
import 'package:inmotion_protocol_example/module/base/base.dart';
import 'package:inmotion_protocol/inmotion_protocol.dart';
import 'package:inmotion_protocol/util/log.dart';
import 'package:inmotion_protocol_example/module/global/global.dart';
import 'package:inmotion_protocol_example/module/widget/prompt.dart';
import 'package:path_provider/path_provider.dart';

class FwUpdateController extends BaseController {
  FwUpdateManager? fwUpdateManager;

  RxString updateStatus = RxString("IDLE");

  BtHandler btHandler = BtHandler();
  ProductProperty? productProperty;

  @override
  void init() {
    InMotionProtocol protocol = Get.find(tag: 'protocol');

    productProperty = Get.find(tag: 'product_property');

    if (productProperty == null) {
      throw InvalidParamException("Couldn't find product property.");
    }

    btHandler.currentDeviceName =
        BleManager().activeDevices.values.first.device.name;
    btHandler.currentDeviceUuid =
        BleManager().activeDevices.values.first.device.id.id;

    fwUpdateManager = protocol.createFwUpdateManager(
        btHandler, ManualHandler(productProperty!));

    MultiDeviceUpdateConfig config = MultiDeviceUpdateConfig();
    // prepareConfigV5(config).then((value) {
    //   fwUpdateManager!.config(config);
    // });

    // prepareConfigV12(config).then((value) {
    //   fwUpdateManager!.config(config);
    // });

    // prepareConfigLE1(config).then((value) {
    //   fwUpdateManager!.config(config);
    // });

    // prepareConfigLR1(config).then((value) {
    //   fwUpdateManager!.config(config);
    // });

    prepareConfigE20(config).then((value) {
      fwUpdateManager!.config(config);
    });
  }

  Future prepareConfigV5(MultiDeviceUpdateConfig config) {
    String bin = "V5_PC.bin"; // 0x21: 1.3.527; 0x22: 1.2.516
    var newVersion = {0x21: '1.3.527', 0x22: '1.2.516'};
    // String bin = "V5E_1.5.412.bin"; // 0x21: 1.5.412; 0x22: 1.2.526
    // var newVersion = {0x21: '1.5.412', 0x22: '1.2.526'};

    return getApplicationDocumentsDirectory().then((dir) {
      // SingleDeviceUpdateConfig singleDeviceUpdateConfig = SingleDeviceUpdateConfig(0, 0x21, File(dir.path + "/fw/$bin"), {0x21: '1.6.14'});
      SingleDeviceUpdateConfig singleDeviceUpdateConfig =
          SingleDeviceUpdateConfig(
              0, 0x21, File(dir.path + "/fw/$bin"), newVersion);
      singleDeviceUpdateConfig.isLinkLayerDevice = false;
      config.putSingleConfig(0x21, singleDeviceUpdateConfig);
      return true;
    });
  }

  Future prepareConfigV10(MultiDeviceUpdateConfig config) {
    // String bin = "V5_PC.bin"; // 0x21: 1.3.527; 0x22: 1.2.516
    // var newVersion = {0x21: '1.3.527', 0x22: '1.2.516'};
    String bin = "V5E_1.5.412.bin"; // 0x21: 1.5.412; 0x22: 1.2.526
    var newVersion = {0x21: '1.5.412', 0x22: '1.2.526'};

    return getApplicationDocumentsDirectory().then((dir) {
      // SingleDeviceUpdateConfig singleDeviceUpdateConfig = SingleDeviceUpdateConfig(0, 0x21, File(dir.path + "/fw/$bin"), {0x21: '1.6.14'});
      SingleDeviceUpdateConfig singleDeviceUpdateConfig =
          SingleDeviceUpdateConfig(
              0, 0x21, File(dir.path + "/fw/$bin"), newVersion);
      singleDeviceUpdateConfig.isLinkLayerDevice = false;
      config.putSingleConfig(0x21, singleDeviceUpdateConfig);
      return true;
    });
  }

  Future prepareConfigV11(MultiDeviceUpdateConfig config) {
    // String hmicBin = "V11_hmic(V1.4.22).bin";
    // String mcBin = "vx_mc(V4.2.24).bin";
    // return getApplicationDocumentsDirectory().then((dir) {
    //   SingleDeviceUpdateConfig configItem1 = SingleDeviceUpdateConfig(0, 0x21, File(dir.path + "/fw/$hmicBin"), {0x21: "1.4.22"});
    //   config.putSingleConfig(0x21, configItem1);
    //
    //   SingleDeviceUpdateConfig configItem2 = SingleDeviceUpdateConfig(0, 0x22, File(dir.path + "/fw/$mcBin"), {0x22: "4.2.24"});
    //   config.putSingleConfig(0x22, configItem2);
    //   return true;
    // });

    // String hmicBin = "V11_hmic(V1.4.19).bin";
    // String mcBin = "vx_mc(V4.2.21).bin";
    String bleBin = "VX_BLE(V1.3.3).bin";
    return getApplicationDocumentsDirectory().then((dir) {
      // SingleDeviceUpdateConfig configItem1 = SingleDeviceUpdateConfig(0, 0x21, File(dir.path + "/fw/$hmicBin"), {0x21: "1.4.19"});
      // config.putSingleConfig(0x21, configItem1);

      // SingleDeviceUpdateConfig configItem2 = SingleDeviceUpdateConfig(0, 0x22, File(dir.path + "/fw/$mcBin"), {0x22: "4.2.21"});
      // config.putSingleConfig(0x22, configItem2);

      SingleDeviceUpdateConfig configItem3 = SingleDeviceUpdateConfig(
          0, 0x23, File(dir.path + "/fw/$bleBin"), {0x23: "1.3.3"});
      config.putSingleConfig(0x23, configItem3);
      return true;
    });
  }

  Future prepareConfigV12(MultiDeviceUpdateConfig config) {
    // String hmicBin = "V11_hmic(V1.4.22).bin";
    // String mcBin = "vx_mc(V4.2.24).bin";
    // return getApplicationDocumentsDirectory().then((dir) {
    //   SingleDeviceUpdateConfig configItem1 = SingleDeviceUpdateConfig(0, 0x21, File(dir.path + "/fw/$hmicBin"), {0x21: "1.4.22"});
    //   config.putSingleConfig(0x21, configItem1);
    //
    //   SingleDeviceUpdateConfig configItem2 = SingleDeviceUpdateConfig(0, 0x22, File(dir.path + "/fw/$mcBin"), {0x22: "4.2.24"});
    //   config.putSingleConfig(0x22, configItem2);
    //   return true;
    // });

    String hmicBin = "V12/V12_hmic(ID=3)(V1.6.19)_crc.bin";
    String mcBin = "V12/vx_mc(V4.104.7)gd32f30x.bin";
    String bleBin = "V12/V12_BLE(V2.1.59).bin";
    return getApplicationDocumentsDirectory().then((dir) {
      SingleDeviceUpdateConfig configItem1 = SingleDeviceUpdateConfig(
          0, 0x21, File(dir.path + "/fw/$hmicBin"), {0x21: "1.6.19"});
      config.putSingleConfig(0x21, configItem1);

      SingleDeviceUpdateConfig configItem2 = SingleDeviceUpdateConfig(
          1, 0x22, File(dir.path + "/fw/$mcBin"), {0x22: "4.104.7"});
      config.putSingleConfig(0x22, configItem2);

      SingleDeviceUpdateConfig configItem3 = SingleDeviceUpdateConfig(
          2, 0x23, File(dir.path + "/fw/$bleBin"), {0x23: "2.1.59"});
      config.putSingleConfig(0x23, configItem3);
      return true;
    });
  }

  Future prepareConfigLE1(MultiDeviceUpdateConfig config) {
    // String hmicBin = "V11_hmic(V1.4.22).bin";
    // String mcBin = "vx_mc(V4.2.24).bin";
    // return getApplicationDocumentsDirectory().then((dir) {
    //   SingleDeviceUpdateConfig configItem1 = SingleDeviceUpdateConfig(0, 0x21, File(dir.path + "/fw/$hmicBin"), {0x21: "1.4.22"});
    //   config.putSingleConfig(0x21, configItem1);
    //
    //   SingleDeviceUpdateConfig configItem2 = SingleDeviceUpdateConfig(0, 0x22, File(dir.path + "/fw/$mcBin"), {0x22: "4.2.24"});
    //   config.putSingleConfig(0x22, configItem2);
    //   return true;
    // });

    String mcBin = "CNF566_LE1_1217_V1.2.16.bin";
    String bleBin = "HW8086_CNF566_LE1_V1.2.37_20221107.bin";
    return getApplicationDocumentsDirectory().then((dir) {
      SingleDeviceUpdateConfig configItem2 = SingleDeviceUpdateConfig(
          0,
          DeviceUtils.lorinScooterBtDevice,
          File(dir.path + "/fw/$bleBin"),
          {DeviceUtils.lorinScooterBtDevice: "1.2.37"});
      configItem2.isLinkLayerDevice = true;
      configItem2.autoSetupTimeoutMills(productProperty!.model);
      config.putSingleConfig(DeviceUtils.lorinScooterBtDevice, configItem2);

      SingleDeviceUpdateConfig configItem1 = SingleDeviceUpdateConfig(
          1,
          DeviceUtils.lorinScooterMcDevice,
          File(dir.path + "/fw/$mcBin"),
          {DeviceUtils.lorinScooterMcDevice: "1.2.16"});
      configItem1.autoSetupTimeoutMills(productProperty!.model);

      config.putSingleConfig(DeviceUtils.lorinScooterMcDevice, configItem1);

      return true;
    });
  }

  Future prepareConfigLR1(MultiDeviceUpdateConfig config) {
    // String bleBin = "LX211_ECU_20230327_V1.0.18.bin";
    // String bleBin = "LX211_ECU_20230328_V1.0.19.bin";
    // String bleBin = "LX211_ECU_20230331_V1.0.21.bin";
    String mc1Bin = "LX211_MC1_20230327_V1.0.7.bin";
    String mc2Bin = "LX211_MC2_20230327_V1.0.7.bin";
    // String mc1Bin = "LX211_MC1_20230216_V1.0.5.bin";
    // String mc2Bin = "LX211_MC2_20230216_V1.0.5.bin";
    return getApplicationDocumentsDirectory().then((dir) {
      // SingleDeviceUpdateConfig configItem1 = SingleDeviceUpdateConfig(0, DeviceUtils.lorinLR1BtDevice, File(dir.path + "/fw/$bleBin"), {DeviceUtils.lorinLR1BtDevice: "1.0.21"});
      // configItem1.isLinkLayerDevice = true;
      // configItem1.autoSetupTimeoutMills(productProperty!.model);
      // config.putSingleConfig(DeviceUtils.lorinLR1BtDevice, configItem1);

      SingleDeviceUpdateConfig configItem2 = SingleDeviceUpdateConfig(
          1,
          DeviceUtils.lorinLR1Mc1Device,
          File(dir.path + "/fw/$mc1Bin"),
          {DeviceUtils.lorinLR1Mc1Device: "1.0.7"});
      configItem2.isLinkLayerDevice = false;
      configItem2.autoSetupTimeoutMills(productProperty!.model);
      config.putSingleConfig(DeviceUtils.lorinLR1Mc1Device, configItem2);

      SingleDeviceUpdateConfig configItem3 = SingleDeviceUpdateConfig(
          2,
          DeviceUtils.lorinLR1Mc2Device,
          File(dir.path + "/fw/$mc2Bin"),
          {DeviceUtils.lorinLR1Mc2Device: "1.0.7"});
      configItem3.isLinkLayerDevice = false;
      configItem3.autoSetupTimeoutMills(productProperty!.model);
      config.putSingleConfig(DeviceUtils.lorinLR1Mc2Device, configItem3);

      return true;
    });
  }

  Future prepareConfigE20(MultiDeviceUpdateConfig config) {
    // String mc1Bin = "LX211_MC1_20230327_V1.0.7.bin";
    // String mc2Bin = "LX211_MC2_20230327_V1.0.7.bin";
    String bleBin = "LX211_MC2_20230327_V1.0.7.bin";
    // String mc1Bin = "LX211_MC1_20230216_V1.0.5.bin";
    // String mc2Bin = "LX211_MC2_20230216_V1.0.5.bin";
    return getApplicationDocumentsDirectory().then((dir) {
      SingleDeviceUpdateConfig configItem1 = SingleDeviceUpdateConfig(
          0,
          DeviceUtils.lorinVBtDevice,
          File(dir.path + "/fw/$bleBin"),
          {DeviceUtils.lorinVBtDevice: "1.0.6"});
      configItem1.isLinkLayerDevice = true;
      configItem1.autoSetupTimeoutMills(productProperty!.model);
      config.putSingleConfig(DeviceUtils.lorinVBtDevice, configItem1);

      // SingleDeviceUpdateConfig configItem2 = SingleDeviceUpdateConfig(1, DeviceUtils.lorinLR1Mc1Device, File(dir.path + "/fw/$mc1Bin"), {DeviceUtils.lorinLR1Mc1Device: "1.0.7"});
      // configItem2.isLinkLayerDevice = false;
      // configItem2.autoSetupTimeoutMills(productProperty!.model);
      // config.putSingleConfig(DeviceUtils.lorinLR1Mc1Device, configItem2);
      //
      // SingleDeviceUpdateConfig configItem3 = SingleDeviceUpdateConfig(2, DeviceUtils.lorinLR1Mc2Device, File(dir.path + "/fw/$mc2Bin"), {DeviceUtils.lorinLR1Mc2Device: "1.0.7"});
      // configItem3.isLinkLayerDevice = false;
      // configItem3.autoSetupTimeoutMills(productProperty!.model);
      // config.putSingleConfig(DeviceUtils.lorinLR1Mc2Device, configItem3);

      return true;
    });
  }

  void startUpdate() {
    Global.getInstance().put("fw_updating", true);
    Global.getInstance().put("bt_state_change_handler",
        (connected, deviceIdentifier, deviceName) {
      print(
          "bt_state_change_handler called, connectedCallback of btHandle is null?${btHandler.connectedCallback == null}, connected?$connected");
      if (btHandler.connectedCallback != null)
        btHandler.connectedCallback!(connected, deviceIdentifier, deviceName);
    });
    fwUpdateManager!.start().listen((event) {
      LogUtils.d(
          "FwUpdateEvent, ${event.toString()}, desc: ${getUpdateStatusDesc(event)}");
      switch (event.step) {
        case FwUpdateEvent.stepPreparing:
          updateStatus.value = "正在准备";
          break;
        case FwUpdateEvent.stepFileTransferring:
          event as FwUpdateFileTransferringEvent;
          updateStatus.value =
              "文件传输中，progress: ${event.progress()}, total: ${event.total()}, aveSpeed: ${event.aveSpeed() / 1000}kb/s, rtSpeed: ${event.rtSpeed() / 1000}kb/s, remain seconds: ${event.remainSeconds()}, cost seconds: ${event.costSeconds()}";
          break;
        case FwUpdateEvent.stepFileChecking:
          updateStatus.value = "正在进行文件校验";
          break;
        case FwUpdateEvent.stepWaiting4Update:
          updateStatus.value = "正在等待设备升级";
          break;
        case FwUpdateEvent.stepSuccess:
          updateStatus.value = "升级成功";
          Global.getInstance().remove("fw_updating");
          Global.getInstance().remove("bt_state_change_handler");
          break;
      }
    }).onError((error) {
      Global.getInstance().remove("fw_updating");
      Global.getInstance().remove("bt_state_change_handler");

      if (error is FwUpdateError) {
        LogUtils.e("Caught FwUpdateError, ${error.getErrorDesc()}");
        updateStatus.value = "Caught FwUpdateError, ${error.getErrorDesc()}";

        if (error.isNotNowError()) {
          LogUtils.e("电机在工作状态");
          updateStatus.value = "The motor is working!!!!";
        }
        return;
      }
      if (error is InvalidDeviceStateException) {
        LogUtils.e("Caught InvalidDeviceStateException");
        updateStatus.value = "蓝牙已断开";
        return;
      }
      updateStatus.value = "Caught unknown error, $error";
      LogUtils.d('Caught error in fw update page, $error');
    });
  }

  void cancelUpdate() {
    fwUpdateManager!.cancel();
  }

  String getUpdateStatusDesc(FwUpdateEvent event) {
    return event.toString();
  }
}

class BtHandler extends LinkLayerDeviceHandler {
  String currentDeviceName = "";
  String currentDeviceUuid = "";

  /// 蓝牙连接成功失败回调
  Function? connectedCallback;

  @override
  void connect(Function connectedCallback,
      {Duration scanTimeout = const Duration(seconds: 3),
      Duration connectTimeout = const Duration(seconds: 10)}) {
    this.connectedCallback = connectedCallback;
    BleManager().connect(
        name: currentDeviceName,
        id: currentDeviceUuid,
        scanTimeout: scanTimeout,
        timeout: connectTimeout,
        timeoutCallback: () {
          print("bt connection timeout");

          /// 未启动连接，可能为未搜到指定设备
          /// 回调连接失败
          connectedCallback(false, currentDeviceUuid, currentDeviceName);
        });
  }

  @override
  void disconnect() {
    BleManager bleManager = BleManager();
    if (bleManager.activeDevices.isNotEmpty) {
      BluetoothDevice current = bleManager.activeDevices.values.first.device;
      currentDeviceName = current.name;
      currentDeviceUuid = current.id.id;
      BleManager().disconnect(device: current);
      return;
    }
    throw InvalidDeviceStateException('There is no activated device.');
  }

  @override
  void updateLinkConfig(Map<String, dynamic> params) {
    if (!params.containsKey('ble_uuid_config')) return;

    BleManager bleManager = BleManager();

    Map<String, dynamic> bleUUIDConfig = params['ble_uuid_config'];
    BleUuid bleUUID = BleUuid(
        bleUUIDConfig['write_service_uuid'],
        bleUUIDConfig['write_characteristic_uuid'],
        bleUUIDConfig['notify_service_uuid'],
        bleUUIDConfig['notify_characteristic_uuid']);

    if (bleUUIDConfig['use_config']) {
      List<BleUuid> uuidList = [];
      uuidList.add(bleUUID);
      uuidList.addAll(bleManager.supportedUuidList);
      bleManager.supportedUuidList = uuidList;
    } else {
      List<BleUuid> uuidList = [];

      for (int i = 0; i < bleManager.supportedUuidList.length; i++) {
        if (bleManager.supportedUuidList[i] == bleUUID) continue;
        uuidList.add(bleManager.supportedUuidList[i]);
      }
      bleManager.supportedUuidList = uuidList;
    }
  }
}

class ManualHandler extends RequiredManualOperationHandler {
  ManualHandler(ProductProperty productProperty) : super(productProperty);

  @override
  void waitingOperation(Function f) {
    print("waitingOperation called.");
    if (productProperty.model.isLE1Series() ||
        productProperty.model.isLR1Series()) {
      // productProperty.model.isRSVeloSeries() 不需要这个步骤，自己会重启
      Prompt.showDialog("在仪表显示屏熄灭后，长按车辆开机按键开机，然后点击下一步", () {
        f(true);
      });
    }
  }
}

class InvalidDeviceStateException implements Exception {
  factory InvalidDeviceStateException([var message]) =>
      InvalidDeviceStateException(message);
}
